package db

import (
	"os"
	"github.com/Philio/GoMySQL"
	"gocase/config"
	"time"
)

type User struct {
	Nickname string
	Activity uint
	Create   uint
}

type Topic struct {
	Name string
	Comment string
}

type Case struct {
	Id uint
	Title string
	Body string
	Author string
	Activity uint
	Create uint
	Answerer string
}

var conn *mysql.Client

func Dial() (err os.Error) {
	conn, err = mysql.DialTCP(config.Host, config.User, config.Passwd, config.Schema)
	if err != nil {
		return err
	}
	conn.SetAutoCommit(true)
	return
}

func GetUser(nm string) (usr *User) {
	stmt, _ := query("select * from users where nickname = ?", nm)
	usr = bindUser(stmt)
	if !fetchOne(stmt) {
		usr = nil
	}
	return
}

// Returns true if added.
func AddUser(nm string) bool {
	t := time.Seconds()
	_, err := query("insert into users values (?, ?, ?)",
		nm, t, t)
	return err == nil
}

// Sets user's activity to now.
func UpdateUser(nm string) {
	query("update users set activity = ? where nickname = ?",
		time.Seconds(), nm)
}

func GetTopics() (topics []Topic) {
	stmt, _ := query("select * from topics")
	topic := bindTopic(stmt)
	fetch(stmt, func() {
		topics = append(topics, *topic)
	})
	return
}

func GetCases() (cases []Case) {
	stmt, _ := query("select * from cases")
	cs := bindCase(stmt)
	fetch(stmt, func() {
		cases = append(cases, *cs)
	})
	return
}

func GetCase(id uint) (cs *Case) {
	stmt, _ := query("select * from cases where id = ?", id)
	cs = bindCase(stmt)
	if !fetchOne(stmt) {
		cs = nil
	}
	return
}

func bindUser(stmt *mysql.Statement) *User {
	var usr User
	stmt.BindResult(&usr.Nickname, &usr.Activity, &usr.Create)
	return &usr
}

func bindTopic(stmt *mysql.Statement) *Topic {
	var topic Topic
	stmt.BindResult(&topic.Name, &topic.Comment)
	return &topic
}

func bindCase(stmt *mysql.Statement) *Case {
	var cs Case
	stmt.BindResult(&cs.Id, &cs.Title, &cs.Body, &cs.Author,
		&cs.Activity, &cs.Create, &cs.Answerer)
	return &cs
}

func query(sql string, params ...interface{}) (stmt *mysql.Statement, err os.Error) {
	stmt, err = conn.Prepare(sql)
	if err != nil {
		return
	}
	if len(params) > 0 {
		stmt.BindParams(params...)
	}
	err = stmt.Execute()
	return
}

func fetch(stmt *mysql.Statement, fetched func()) {
	for {
		if eof, _ := stmt.Fetch(); eof {
			break
		}
		fetched()
	}
}

func fetchOne(stmt *mysql.Statement) bool {
	eof, _ := stmt.Fetch()
	return !eof
}
